package org.farng.mp3.id3;

import org.farng.mp3.AbstractMP3Tag;
import org.farng.mp3.TagException;
import org.farng.mp3.TagNotFoundException;
import org.farng.mp3.TagOptionSingleton;
import org.farng.mp3.TagUtility;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;

import java.util.Iterator;


/**
 * Title:       ID3v1_1 Description: This class is for a ID3v1.1 Tag Copyright:
 * Copyright (c) 2002 Company:
 *
 * @author Eric Farng
 * @version $Revision: 1.7 $
 */
public class ID3v1_1 extends ID3v1 {
    /** DOCUMENT ME! */
    protected byte track = -1;

    /**
     * Creates a new ID3v1_1 object.
     */
    public ID3v1_1() {}

    public ID3v1_1(ID3v1_1 copyObject) {
        super(copyObject);
        this.track = copyObject.track;
    }

    /**
     * Creates a new ID3v1_1 object.
     *
     * @param mp3tag DOCUMENT ME!
     *
     * @throws UnsupportedOperationException DOCUMENT ME!
     */
    public ID3v1_1(AbstractMP3Tag mp3tag) {
        if (mp3tag != null) {
            if (mp3tag instanceof ID3v1) {
                if (mp3tag instanceof ID3v1_1) {
                    throw new UnsupportedOperationException("Copy Constructor not called. Please type cast the argument");
                }

                if (mp3tag instanceof ID3v1) {
                    // id3v1_1 objects are also id3v1 objects
                    ID3v1 id3old = (ID3v1) mp3tag;
                    this.title   = new String(id3old.title);
                    this.artist  = new String(id3old.artist);
                    this.album   = new String(id3old.album);
                    this.comment = new String(id3old.comment);
                    this.year    = new String(id3old.year);
                    this.genre   = id3old.genre;
                }
            } else {
                // first change the tag to ID3v2_4 tag.
                // id3v2_4 can take any tag.
                ID3v2_4 id3tag;
                id3tag = new ID3v2_4(mp3tag);

                ID3v2_4Frame frame;
                String       text;

                if (id3tag.hasFrame("TIT2")) {
                    frame      = (ID3v2_4Frame) id3tag.getFrame("TIT2");
                    text       = (String) ((FrameBodyTIT2) frame.getBody()).getText();
                    this.title = TagUtility.truncate(text, 30);
                }

                if (id3tag.hasFrame("TPE1")) {
                    frame       = (ID3v2_4Frame) id3tag.getFrame("TPE1");
                    text        = (String) ((FrameBodyTPE1) frame.getBody()).getText();
                    this.artist = TagUtility.truncate(text, 30);
                }

                if (id3tag.hasFrame("TALB")) {
                    frame      = (ID3v2_4Frame) id3tag.getFrame("TALB");
                    text       = (String) ((FrameBodyTALB) frame.getBody()).getText();
                    this.album = TagUtility.truncate(text, 30);
                }

                if (id3tag.hasFrame("TDRC")) {
                    frame     = (ID3v2_4Frame) id3tag.getFrame("TDRC");
                    text      = (String) ((FrameBodyTDRC) frame.getBody()).getText();
                    this.year = TagUtility.truncate(text, 4);
                }

                if (id3tag.hasFrameOfType("COMM")) {
                    Iterator iterator = id3tag.getFrameOfType("COMM");
                    text = "";

                    while (iterator.hasNext()) {
                        frame = (ID3v2_4Frame) iterator.next();

                        text += (((FrameBodyCOMM) frame.getBody()).getText() + " ");
                    }

                    this.comment = TagUtility.truncate(text, 28);
                }

                if (id3tag.hasFrame("TCON")) {
                    frame = (ID3v2_4Frame) id3tag.getFrame("TCON");
                    text  = (String) ((FrameBodyTCON) frame.getBody()).getText();

                    try {
                        this.genre = (byte) TagUtility.findNumber(text);
                    } catch (TagException ex) {}
                }

                if (id3tag.hasFrame("TRCK")) {
                    frame = (ID3v2_4Frame) id3tag.getFrame("TRCK");
                    text  = (String) ((FrameBodyTRCK) frame.getBody()).getText();

                    try {
                        this.track = (byte) TagUtility.findNumber(text);
                    } catch (TagException ex) {}
                }
            }
        }
    }

    /**
     * Creates a new ID3v1_1 object.
     *
     * @param file DOCUMENT ME!
     *
     * @throws TagNotFoundException DOCUMENT ME!
     * @throws IOException DOCUMENT ME!
     */
    public ID3v1_1(RandomAccessFile file)
            throws TagNotFoundException, IOException {
        this.read(file);
    }

    /**
     * DOCUMENT ME!
     *
     * @param comment DOCUMENT ME!
     */
    public void setComment(String comment) {
        this.comment = TagUtility.truncate(comment, 28);
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public String getComment() {
        return comment;
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public String getIdentifier() {
        return "ID3v1_1.10";
    }

    /**
     * DOCUMENT ME!
     *
     * @param track DOCUMENT ME!
     */
    public void setTrack(byte track) {
        this.track = track;
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public byte getTrack() {
        return track;
    }

    /**
     * DOCUMENT ME!
     *
     * @param tag DOCUMENT ME!
     */
    public void append(AbstractMP3Tag tag) {
        ID3v1_1 oldTag = this;
        ID3v1_1 newTag = null;

        if (tag != null) {
            if (tag instanceof ID3v1_1) {
                newTag = (ID3v1_1) tag;
            } else {
                newTag = new ID3v1_1(tag);
            }

            if (tag instanceof org.farng.mp3.lyrics3.AbstractLyrics3) {
                TagOptionSingleton.getInstance().setId3v1SaveTrack(false);
            }

            oldTag.track = (TagOptionSingleton.getInstance().isId3v1SaveTrack() && (oldTag.track <= 0)) ? newTag.track : oldTag.track;

            // we don't need to reset the tag options because
            // we want to save all fields (default)
        }

        // we can't send newTag here because we need to keep the lyrics3
        // class type ... check super.append and you'll see what i mean.
        super.append(tag);
    }

    /**
     * DOCUMENT ME!
     *
     * @param obj DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */

//    public void append(RandomAccessFile file)
//                throws IOException, TagException {
//        ID3v1_1 oldTag;
//
//        try {
//            oldTag = new ID3v1_1(file);
//            oldTag.append(this);
//            oldTag.write(file);
//        } catch (TagNotFoundException ex) {
//            oldTag = null;
//        }
//    }

    /**
     * DOCUMENT ME!
     *
     * @param obj DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public boolean equals(Object obj) {
        if ((obj instanceof ID3v1_1) == false) {
            return false;
        }

        ID3v1_1 object = (ID3v1_1) obj;

        if (this.track != object.track) {
            return false;
        }

        return super.equals(obj);
    }

    /**
     * DOCUMENT ME!
     *
     * @param tag DOCUMENT ME!
     */
    public void overwrite(AbstractMP3Tag tag) {
        ID3v1_1 oldTag = this;
        ID3v1_1 newTag = null;

        if (tag != null) {
            if (tag instanceof ID3v1_1) {
                newTag = (ID3v1_1) tag;
            } else {
                newTag = new ID3v1_1(tag);
            }

            if (tag instanceof org.farng.mp3.lyrics3.AbstractLyrics3) {
                TagOptionSingleton.getInstance().setId3v1SaveTrack(false);
            }

            oldTag.track = TagOptionSingleton.getInstance().isId3v1SaveTrack() ? newTag.track : oldTag.track;

            // we don't need to reset the tag options because
            // we want to save all fields (default)
        }

        super.overwrite(newTag);
    }

    /**
     * DOCUMENT ME!
     *
     * @param file DOCUMENT ME!
     *
     * @throws IOException DOCUMENT ME!
     * @throws IOException DOCUMENT ME!
     */

//    public void overwrite(RandomAccessFile file)
//                   throws IOException, TagException {
//        ID3v1_1 oldTag;
//
//        try {
//            oldTag = new ID3v1_1(file);
//            oldTag.overwrite(this);
//            oldTag.write(file);
//        } catch (TagNotFoundException ex) {
//            super.overwrite(file);
//        }
//    }

    /**
     * DOCUMENT ME!
     *
     * @param file DOCUMENT ME!
     *
     * @throws TagNotFoundException DOCUMENT ME!
     * @throws IOException DOCUMENT ME!
     */
    public void read(RandomAccessFile file)
              throws TagNotFoundException, IOException {
        byte[] buffer = new byte[30];

        if (this.seek(file) == false) {
            throw new TagNotFoundException("ID3v1.1 tag not found");
        }

        file.read(buffer, 0, 30);
        title = new String(buffer, 0, 30).trim();

        file.read(buffer, 0, 30);
        artist = new String(buffer, 0, 30).trim();

        file.read(buffer, 0, 30);
        album = new String(buffer, 0, 30).trim();

        file.read(buffer, 0, 4);
        year = new String(buffer, 0, 4).trim();

        file.read(buffer, 0, 28);
        comment = new String(buffer, 0, 28).trim();

        // if this value is zero, then check the next value
        // to see if it's the track number. ID3v1.1
        file.read(buffer, 0, 2);

        if (buffer[0] == 0) {
            track = buffer[1];
        } else {
            throw new TagNotFoundException("ID3v1.1 Tag Not found");
        }

        file.read(buffer, 0, 1);
        genre = buffer[0];
    }

    /**
     * DOCUMENT ME!
     *
     * @param file DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     *
     * @throws IOException DOCUMENT ME!
     */
    public boolean seek(RandomAccessFile file)
                 throws IOException {
        byte[] buffer = new byte[3];

        if (file.length() < 128) {
            return false;
        }

        // Check for the empty byte before the TRACK
        file.seek(file.length() - 3);
        buffer[0] = file.readByte();

        if (buffer[0] != 0) {
            return false;
        }

        // If there's a tag, it's 128 bytes long and we'll find the tag
        file.seek(file.length() - 128);

        // read the TAG value
        file.read(buffer, 0, 3);

        String tag = new String(buffer, 0, 3);

        return tag.equals("TAG");
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public String toString() {
        String str = getIdentifier() + " " + this.getSize() + "\n";

        str += ("Title = " + title + "\n");
        str += ("Artist = " + artist + "\n");
        str += ("Album = " + album + "\n");
        str += ("Comment = " + comment + "\n");
        str += ("Year = " + year + "\n");
        str += ("Genre = " + genre + "\n");
        str += ("Track = " + track + "\n");

        return str;
    }

    /**
     * DOCUMENT ME!
     *
     * @param tag DOCUMENT ME!
     */
    public void write(AbstractMP3Tag tag) {
        ID3v1_1 oldTag = this;
        ID3v1_1 newTag = null;

        if (tag != null) {
            if (tag instanceof ID3v1_1) {
                newTag = (ID3v1_1) tag;
            } else {
                newTag = new ID3v1_1(tag);
            }

            oldTag.track = newTag.track;
        }

        super.write(newTag);
    }

    /**
     * DOCUMENT ME!
     *
     * @param file DOCUMENT ME!
     *
     * @throws IOException DOCUMENT ME!
     */
    public void write(RandomAccessFile file)
               throws IOException {
        byte[] buffer = new byte[128];
        Charset cset = Charset.forName(TagOptionSingleton.getInstance().getCharset());
        ByteBuffer tmpBf;
        int    i;
        int    offset = 3;
        String str;

        delete(file);
        file.seek(file.length());

        buffer[0] = (byte) 'T';
        buffer[1] = (byte) 'A';
        buffer[2] = (byte) 'G';

        str = TagUtility.truncate(title, 30);
        tmpBf = cset.encode(str);
        for (i = 0; i < tmpBf.limit(); i++) {
            buffer[i + offset] = tmpBf.array()[i];
        }

        offset += 30;

        str = TagUtility.truncate(artist, 30);
        tmpBf = cset.encode(str);
        for (i = 0; i < tmpBf.limit(); i++) {
            buffer[i + offset] = tmpBf.array()[i];
        }

        offset += 30;

        str = TagUtility.truncate(album, 30);
        tmpBf = cset.encode(str);
        for (i = 0; i < tmpBf.limit(); i++) {
            buffer[i + offset] = tmpBf.array()[i];
        }

        offset += 30;

        str = TagUtility.truncate(year, 4);
        tmpBf = cset.encode(str);
        for (i = 0; i < tmpBf.limit(); i++) {
            buffer[i + offset] = tmpBf.array()[i];
        }

        offset += 4;

        str = TagUtility.truncate(comment, 28);
        tmpBf = cset.encode(str);
        for (i = 0; i < tmpBf.limit(); i++) {
            buffer[i + offset] = tmpBf.array()[i];
        }

        offset += 28;

        buffer[++offset] = track; // skip one byte extra blank for 1.1 definition
        buffer[++offset] = genre;

        file.write(buffer);
    }
}